#pragma once
#include <iostream>
#include <memory>
#include <string>
#include <functional>
#include <unistd.h>
#include <sys/epoll.h>
#include "Socket.hpp"
#include "Log.hpp"
#include "Error.hpp"
#include "InetAddr.hpp"
using namespace sock_ns;
using namespace ns_log;

class EpollServer
{
    static const int epoll_size = 128; // 无效参数,大于0即可
    static const int gnums = 512;
    static const int recv_mem_size = 4096;
public:
    EpollServer(uint16_t port) : _port(port), _listensocket(std::make_unique<Tcp_socket>())
    {
        _listensocket->BuildListenSocket(_port);
        _epollfd = ::epoll_create(epoll_size);
        if (_epollfd < 0)
        {
            LOG(FATAL, "epoll create error!\n");
            exit(EPOLL_CREATE_ERROR);
        }
        LOG(INFO, "epoll create success!,epollfd = %d\n", _epollfd);
    }
    std::string SetEventStr(int flag)
    {
        std::string res;
        if (flag & EPOLLIN)
            res += "EPOLLIN";
        if (flag & EPOLLOUT)
            res += "| EPOLLOUT";
        return res;
    }
    void InitServer()
    {
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _listensocket->SockFd();
        int res = ::epoll_ctl(_epollfd, EPOLL_CTL_ADD, _listensocket->SockFd(), &ev);
        if (res < 0)
        {
            LOG(FATAL, "epoll_ctl error!\n");
            exit(EPOLL_CTL_ERROR);
        }
        LOG(INFO, "add sockfd success,newfd = %d", _listensocket->SockFd());
    }
    void Accepter()
    {
        // 处理新的sockfd
        InetAddr addr;
        int clientfd = _listensocket->AcceptSocket(&addr);
        if (clientfd < 0)
        {
            LOG(ERROR, "use AcceptSocket() get a fd has error!\n"); // 英语学渣~QAQ
            return;
        }
        LOG(INFO, "get a new link,addr = %s\n", addr.GetAddrStr().c_str());
        // 添加进epoll
        struct epoll_event ev;
        ev.data.fd = clientfd;
        ev.events = EPOLLIN;
        int res = ::epoll_ctl(_epollfd, EPOLL_CTL_ADD, clientfd, &ev);
        if (res < 0)
        {
            LOG(ERROR, "epoll ctl error!\n");
            return;
        }
    }
    void HandlerIo(int fd)
    {
        // 处理正常IO
        char buffer[recv_mem_size];
        ssize_t res = ::recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (res > 0)
        {
            buffer[res] = 0;
            LOG(INFO, "recv client info: %s\n", buffer);
            std::string resp = "HTTP/1.0 200 OK\r\n";
            resp += "Content-Type:text/html\r\n\r\n";
            resp += "<html><body><h1>Hello Word</h1></body></html>\r\n";
            ::send(fd, resp.c_str(), resp.size(), 0);
        }
        else if (res == 0)
        {
            // 先移除epoll中对应的fd
            LOG(INFO, "client quit...\n");
            int n = ::epoll_ctl(_epollfd, EPOLL_CTL_DEL, fd, nullptr);
            if (n < 0)
            {
                LOG(ERROR, "delete client_fd error!\n");
                ::close(fd);
                return;
            }
            ::close(fd);
        }
        else
        {
            LOG(ERROR, "recv error!\n");
            ::epoll_ctl(_epollfd, EPOLL_CTL_DEL, fd, nullptr);
            ::close(fd);
            return;
        }
    }
    void HandlerEvents(int n)
    {
        for (int i = 0; i < n; i++)
        {
            int fd = ev_array[i].data.fd;
            uint32_t events = ev_array[i].events;
            if (events & EPOLLIN)
            {
                // 判断是是什么类型的套接字
                if (fd == _listensocket->SockFd())
                {
                    Accepter();
                }
                else
                {
                    HandlerIo(fd);
                }
            }
        }
    }
    void Loop()
    {
        int timeout = -1;
        while (true)
        {
            int res = ::epoll_wait(_epollfd, ev_array, gnums, timeout);
            switch (res)
            {
            case 0:
                LOG(INFO, "timeout...\n");
                break;
            case -1:
                LOG(ERROR, "epoll_wait error!\n");
                break;
            default:
                LOG(INFO, "有事件就绪,请处理!\n");
                HandlerEvents(res);
                break;
            }
        }
    }
    ~EpollServer()
    {
        if (_epollfd > 0)
            ::close(_epollfd);
        _listensocket->Close();
    }

private:
    uint16_t _port;
    int _epollfd;
    std::unique_ptr<Socket> _listensocket;
    struct epoll_event ev_array[gnums];
};